import React, { memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';

import type {
    AriaSortState,
    HeaderCellCtrl,
    HeaderStyle,
    IHeader,
    IHeaderCellComp,
    UserCompDetails,
} from 'ag-grid-community';
import { CssClassManager, _EmptyBean, _removeAriaSort, _setAriaSort } from 'ag-grid-community';

import { BeansContext } from '../beansContext';
import { showJsComp } from '../jsComp';
import { isComponentStateless } from '../utils';

const HeaderCellComp = ({ ctrl }: { ctrl: HeaderCellCtrl }) => {
    const isAlive = ctrl.isAlive();

    const { context } = useContext(BeansContext);
    const [userCompDetails, setUserCompDetails] = useState<UserCompDetails>();
    const [userStyles, setUserStyles] = useState<HeaderStyle>();

    const compBean = useRef<_EmptyBean>();
    const eGui = useRef<HTMLDivElement | null>(null);
    const eResize = useRef<HTMLDivElement>(null);
    const eHeaderCompWrapper = useRef<HTMLDivElement>(null);
    const userCompRef = useRef<IHeader>();

    const cssManager = useRef<CssClassManager>();
    if (isAlive && !cssManager.current) {
        cssManager.current = new CssClassManager(() => eGui.current);
    }
    const setRef = useCallback((eRef: HTMLDivElement | null) => {
        eGui.current = eRef;
        if (!eRef || !ctrl.isAlive() || context.isDestroyed()) {
            compBean.current = context.destroyBean(compBean.current);
            return;
        }

        compBean.current = context.createBean(new _EmptyBean());

        const refreshSelectAllGui = () => {
            const selectAllGui = ctrl.getSelectAllGui();
            if (selectAllGui) {
                eResize.current?.insertAdjacentElement('afterend', selectAllGui);
                compBean.current!.addDestroyFunc(() => selectAllGui.remove());
            }
        };

        const compProxy: IHeaderCellComp = {
            setWidth: (width: string) => {
                if (eGui.current) {
                    eGui.current.style.width = width;
                }
            },
            toggleCss: (name: string, on: boolean) => cssManager.current!.toggleCss(name, on),
            setUserStyles: (styles: HeaderStyle) => setUserStyles(styles),
            setAriaSort: (sort?: AriaSortState) => {
                if (eGui.current) {
                    sort ? _setAriaSort(eGui.current, sort) : _removeAriaSort(eGui.current);
                }
            },
            setUserCompDetails: (compDetails: UserCompDetails) => setUserCompDetails(compDetails),
            getUserCompInstance: () => userCompRef.current || undefined,
            refreshSelectAllGui,
            removeSelectAllGui: () => ctrl.getSelectAllGui()?.remove(),
        };

        ctrl.setComp(compProxy, eRef, eResize.current!, eHeaderCompWrapper.current!, compBean.current);

        refreshSelectAllGui();
    }, []);

    // js comps
    useLayoutEffect(
        () => showJsComp(userCompDetails, context, eHeaderCompWrapper.current!, userCompRef),
        [userCompDetails]
    );

    // add drag handling, must be done after component is added to the dom
    useEffect(() => {
        ctrl.setDragSource(eGui.current!);
    }, [userCompDetails]);

    const userCompStateless = useMemo(() => {
        const res = userCompDetails?.componentFromFramework && isComponentStateless(userCompDetails.componentClass);
        return !!res;
    }, [userCompDetails]);

    const reactUserComp = userCompDetails?.componentFromFramework;
    const UserCompClass = userCompDetails?.componentClass;

    return (
        <div ref={setRef} style={userStyles} className="ag-header-cell" role="columnheader">
            <div ref={eResize} className="ag-header-cell-resize" role="presentation"></div>
            <div ref={eHeaderCompWrapper} className="ag-header-cell-comp-wrapper" role="presentation">
                {reactUserComp ? (
                    userCompStateless ? (
                        <UserCompClass {...userCompDetails!.params} />
                    ) : (
                        <UserCompClass {...userCompDetails!.params} ref={userCompRef} />
                    )
                ) : null}
            </div>
        </div>
    );
};

export default memo(HeaderCellComp);
